package edu.temple.mobile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import edu.temple.mobile.objectClasses.BusRoutes;
import edu.temple.mobile.parsing.RouteCoord;
import edu.temple.mobile.parsing.helpers.TransitViewXMLHelper;
import edu.temple.mobile.helpers.WebService;
import edu.temple.mobile.maps.LocationItemizedOverlay;
import edu.temple.mobile.maps.RouteOverlay;
import edu.temple.mobile.maps.busOverlays.Bus;
import edu.temple.mobile.maps.busOverlays.JSONResponse;
import edu.temple.mobile.maps.TransitItemizedOverlay;
import edu.temple.mobile.maps.CustomBalloon;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class TransitViewActivity extends MapActivity implements
		LocationListener {

	LinearLayout linearLayout;
	MapView mapView;
	private ArrayList<String> coordinatesArrayList = new ArrayList<String>();
	private String coordinatesString = new String();
	private String[] coordinatesArray;
	private String[][] pairArray;
	private String[][] lngLatArray;
	private int rows;
	private List<Overlay> mapOverlays;
	private LocationItemizedOverlay itemizedoverlay;
	private ImageButton homeButton;
	private TextView title;
	private List<Bus> buses = null;
	private String JSONString;
	private String KMLString;

	// For GPS things
	private LocationManager locationManager;
	private static final long MINIMUM_DISTANCE_CHANGE_FOR_UPDATES = 3; // in
																		// Meters
	private static final long MINIMUM_TIME_BETWEEN_UPDATES = 1000; // in
																	// Milliseconds
	private Drawable gpsDrawable;
	private MyLocationOverlay gpsLoc;

	// For drawing buses and trolleys
	private TransitItemizedOverlay transitBusBlue;
	private TransitItemizedOverlay transitBusRed;
	private TransitItemizedOverlay transitBusYellow;
	private TransitItemizedOverlay transitTrainBlue;
	private TransitItemizedOverlay transitTrainRed;
	private TransitItemizedOverlay transitTrainYellow;
	private Drawable busDrawableBlue;
	private Drawable busDrawableRed;
	private Drawable busDrawableYellow;
	private Drawable trainDrawableBlue;
	private Drawable trainDrawableRed;
	private Drawable trainDrawableYellow;
	
	
	//For determining map position
	private int minLatitude = (int) (+81 * 1E6);
	private int maxLatitude = (int) (-81 * 1E6);
	private int minLongitude = (int) (+181 * 1E6);
	private int maxLongitude = (int) (-181 * 1E6);

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.transitview);

		/** !!!!!!!!!!!!!!!ALL ICON BUTTON CODE */

		/** Home button */
		homeButton = (ImageButton) findViewById(R.id.actionLogoIcon);
		homeButton.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {

				Intent intent = new Intent(TransitViewActivity.this,
						SEPTripActivity.class);
				intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
				startActivity(intent);
			}
		});

		/** Getting last known location */
		locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		Criteria criteria = new Criteria();
		criteria.setAccuracy(Criteria.ACCURACY_FINE);
		criteria.setAltitudeRequired(false);
		Location lastKnownLocation = locationManager
				.getLastKnownLocation(locationManager.getBestProvider(criteria,
						true));
		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
				MINIMUM_TIME_BETWEEN_UPDATES,
				MINIMUM_DISTANCE_CHANGE_FOR_UPDATES,
				this);

		/** Getting route info */
		Bundle bundle = new Bundle();
		bundle = getIntent().getExtras();
		final String route = bundle.getString("shortName");
		final String vehicleType = bundle.getString("vehicle");
		Log.d("message", route);

		/** Setting title */
		title = (TextView) findViewById(R.id.title);
		title.setText(getString(R.string.route) + " " + route);

		mapView = (MapView) findViewById(R.id.mapview);
		mapView.setBuiltInZoomControls(true);

		/** Getting KML from Septa */
		StringBuilder urlString = new StringBuilder();
		urlString.append("http://www3.septa.org/transitview/kml/");
		urlString.append(route);
		urlString.append(".kml");
		
		/** Retrieving Bus JSON stream */
		StringBuilder urlJSONsb = new StringBuilder();
		urlJSONsb.append("http://www3.septa.org/transitview/bus_route_data/");
		urlJSONsb.append(route);
		final String urlJSON = urlJSONsb.toString();
		mapOverlays = mapView.getOverlays();
		
		class parseJSON extends AsyncTask<String, Void, String>{

			@Override
			protected String doInBackground(String... webServiceURL) {
				
				//Instantiate the Web Service Class with he URL of the web service not that you must pass
		        WebService webService = new WebService(webServiceURL[0]);

		        //Pass the parameters if needed , if not then pass dummy one as follows
				Map<String, String> params = new HashMap<String, String>();
				//params.put("", "");
				JSONString = webService.webGet("", params);
				return JSONString;
			}
			
			protected void onPostExecute(String response){
				buses = new ArrayList<Bus>();
				try {
					// Parse Response into our object
					JSONResponse jResponse = new Gson().fromJson(response,
							JSONResponse.class);
					
					if (vehicleType.equals("Bus")) {
						busDrawableBlue = TransitViewActivity.this.getResources().getDrawable(
								R.drawable.ic_transitview_bus_blue);
						busDrawableRed = TransitViewActivity.this.getResources().getDrawable(
								R.drawable.ic_transitview_bus_red);
						busDrawableYellow = TransitViewActivity.this.getResources().getDrawable(
								R.drawable.ic_transitview_bus_yellow);

						transitBusBlue = new TransitItemizedOverlay(busDrawableBlue, mapView);
						transitBusRed = new TransitItemizedOverlay(busDrawableRed, mapView);
						transitBusYellow = new TransitItemizedOverlay(busDrawableYellow, mapView);
						
						drawTransitIcons(jResponse, transitBusRed, transitBusBlue,
								transitBusYellow, vehicleType);
						if (transitBusRed.size() > 0) {
							mapOverlays.add(transitBusRed);
						}
						if (transitBusBlue.size() > 0) {
							mapOverlays.add(transitBusRed);
						}
						if (transitBusYellow.size() > 0) {
							mapOverlays.add(transitBusRed);
						}
						//Not working
						if (transitTrainRed.size() > 0 | transitTrainBlue.size() > 0 | transitTrainYellow.size() > 0){
							Toast.makeText(TransitViewActivity.this,
									"There are currently no buses transmitting their location",
									Toast.LENGTH_LONG).show();
						}
						mapView.invalidate();
					}else if (vehicleType.equals("Trolley")) {
						trainDrawableBlue = TransitViewActivity.this.getResources().getDrawable(
								R.drawable.ic_transitview_train_blue);
						trainDrawableRed = TransitViewActivity.this.getResources().getDrawable(
								R.drawable.ic_transitview_train_red);
						trainDrawableYellow = TransitViewActivity.this.getResources().getDrawable(
								R.drawable.ic_transitview_train_yellow);

						transitTrainBlue = new TransitItemizedOverlay(trainDrawableBlue, mapView);
						transitTrainRed = new TransitItemizedOverlay(trainDrawableRed, mapView);
						transitTrainYellow = new TransitItemizedOverlay(
								trainDrawableYellow, mapView);

						drawTransitIcons(jResponse, transitTrainRed, transitTrainBlue,
								transitTrainYellow, vehicleType);
						if (transitTrainRed.size() > 0) {
							mapOverlays.add(transitTrainRed);
						}
						if (transitTrainBlue.size() > 0) {
							mapOverlays.add(transitTrainRed);
						}
						if (transitTrainYellow.size() > 0) {
							mapOverlays.add(transitTrainRed);
						}
						
						//Not working
						if (transitTrainRed.size() > 0 & transitTrainBlue.size() > 0 & transitTrainYellow.size() > 0){
							Toast.makeText(TransitViewActivity.this,
									"There are currently no trolleys transmitting their location",
									Toast.LENGTH_LONG).show();
						}
						mapView.invalidate();
					}
				}catch (Exception e) {
					Log.e("Error: ", ""+e.getMessage());
				}
			}
		}
		class parseKML extends AsyncTask<String, Void, TransitViewXMLHelper>{

			@Override
			protected TransitViewXMLHelper doInBackground(String... urlKML) {
				// get the kml file from the Septa Server
				HttpURLConnection urlConnection = null;
				URL url = null;
				
				try {
					/** Handling XML */
					SAXParserFactory spf = SAXParserFactory.newInstance();
					SAXParser sp = spf.newSAXParser();
					XMLReader xr = sp.getXMLReader();

					url = new URL(urlKML[0]);

					/** Create handler to handle XML Tags ( extends DefaultHandler ) */
					TransitViewXMLHelper xmlHandler = new TransitViewXMLHelper();
					xr.setContentHandler(xmlHandler);
					xr.parse(new InputSource(url.openStream()));
					Log.d("message", "parse is done");
					return xmlHandler;
				} catch (MalformedURLException e) {
					e.printStackTrace();
					return null;
				} catch (IOException e) {
					e.printStackTrace();
					return null;
				} catch (ParserConfigurationException e) {
					e.printStackTrace();
					return null;
				} catch (SAXException e) {
					e.printStackTrace();
					return null;
				}
				
			}
			
			@Override
			protected void onPostExecute(TransitViewXMLHelper xmlHandler){
				RouteCoord routeCoord = xmlHandler.getRouteCoord();
				coordinatesArrayList = routeCoord.getCoordinates();
				// ArrayList contains a ton of tab and new line characters and all
				// puts the cuts off the coordinates at random points
				// Converting to a long string that can be parsed properly
				// will use the getCoordRows() to get the rows and make an array
				coordinatesString = coordinatesArrayList.toString();
				coordinatesString = coordinatesString.replaceAll("(\\t|\\n|,)", "")
						.replace('[', ' ').replace(']', ' ');
				coordinatesString = coordinatesString.replaceAll(" ", "");
				coordinatesString = coordinatesString.replaceAll("s", " "); // create
																			// proper
																			// spaces
				coordinatesString = coordinatesString.replaceAll("c", ","); // create
																			// proper
																			// commas
				coordinatesString = coordinatesString.replaceAll(",0 ", " "); // gets
																				// rid
																				// of
																				// altitude
																				// information

				coordinatesArray = new String[routeCoord.getCoordRows()];
				coordinatesArray = coordinatesString.split("E");
				rows = coordinatesArray.length;

				pairArray = new String[rows][];

				for (int i = 0; i < rows; i++) {
					pairArray[i] = coordinatesArray[i].split(" ");
				}

				/** for loop that will draw routes */
				for (int i = 0; i < rows; i++) {
					DrawPath(-12303292, pairArray[i]);
				}
				MapController mMapController = mapView.getController();
				mMapController.zoomToSpan((maxLatitude - minLatitude),
						(maxLongitude - minLongitude));
				mMapController.animateTo(new GeoPoint(
						(maxLatitude + minLatitude) / 2,
						(maxLongitude + minLongitude) / 2));
				mapView.invalidate();
				new parseJSON().execute(urlJSON);
			}
		}
		
		new parseKML().execute(urlString.toString());
		
		/**Drawing GPS icon*/
		gpsLoc = new MyLocationOverlay(this, mapView);
		
		mapOverlays.add(gpsLoc);
		mapView.invalidate();

	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	@Override
	protected void onPause() {
		// remove the listener
		locationManager.removeUpdates(this);
		gpsLoc.disableMyLocation();
		super.onPause();
	}

	@Override
	protected void onResume() {
		// add the listener again
		//locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,100000, 100, this);
		gpsLoc.enableMyLocation();
		super.onResume();
	}

	// simple method for counting a substring
	public int countIndexOf(String text, String search) {
		int count = text.split(search).length - 1;
		return count;
	}

	public void DrawPath(int color, String[] pairs) {
		List<GeoPoint> poly = new ArrayList<GeoPoint>(); // geopoints reverse
															// lat and long
		for (int i = 0; i < pairs.length; i++) {
			String[] lngLat = pairs[i].split(",");
			GeoPoint p = new GeoPoint(
					(int) (Double.parseDouble(lngLat[1]) * 1E6),
					(int) (Double.parseDouble(lngLat[0]) * 1E6));
			poly.add(p);

			minLatitude = (minLatitude > (int) (Double.parseDouble(lngLat[1]) * 1E6)) ? (int) (Double
					.parseDouble(lngLat[1]) * 1E6) : minLatitude;
			maxLatitude = (maxLatitude < (int) (Double.parseDouble(lngLat[1]) * 1E6)) ? (int) (Double
					.parseDouble(lngLat[1]) * 1E6) : maxLatitude;

			minLongitude = (minLongitude > (int) (Double.parseDouble(lngLat[0]) * 1E6)) ? (int) (Double
					.parseDouble(lngLat[0]) * 1E6) : minLongitude;
			maxLongitude = (maxLongitude < (int) (Double.parseDouble(lngLat[0]) * 1E6)) ? (int) (Double
					.parseDouble(lngLat[0]) * 1E6) : maxLongitude;
		}
		List overlays = mapView.getOverlays();
		for (int i = 1; i < poly.size(); i++) {
			overlays.add(new RouteOverlay(poly.get(i - 1), poly.get(i), color));
		}
	}

	public void drawTransitIcons(JSONResponse jResponse,
			TransitItemizedOverlay red, TransitItemizedOverlay blue,
			TransitItemizedOverlay yellow, String transit) {

		List<Bus> transitResults = jResponse.bus;
		Resources res = getResources();
		int redint = 0;
		int blueint = 0;
		int yellowint = 0;

		for (Bus result : transitResults) {
			// Vehicle Location
			GeoPoint pointV = new GeoPoint(
					(int) (Double.parseDouble(result.latStr) * 1E6),
					(int) (Double.parseDouble(result.lngStr) * 1E6));
			
			//Creating HTML string for balloon
			//Bus info goes in Title, last updated goes in text
			
			//Destination logic
			String transitDestination;
			
			if (result.DirectionStr.equals("EastBound")
					|| result.DirectionStr.equals("NorthBound")) {
				transitDestination = "<font color=\"#2253a6\" size=\"17dip\"><b>" 
						+ getString(R.string.transitViewDestination) 
						+ "&nbsp;</b>"
						+ result.destinationStr 
						+ "</font><br/>";
			} else if (result.DirectionStr.equals("WestBound")
					|| result.DirectionStr.equals("SouthBound")) {
				transitDestination = "<font color=\"#f84e2f\" size=\"17dip\"><b>" 
						+ getString(R.string.transitViewDestination) 
						+ "&nbsp;</b>"
						+ result.destinationStr 
						+ "</font><br/>";
			} else {
				transitDestination = "";
			}
			
			String blockNumber = "<b>" + getString(R.string.transitViewBlockNumber) + "&nbsp;</b>"
					+ result.BlockIDStr;
			String busNumber = "<b>" + getString(R.string.transitViewBusNumber) + "&nbsp;</b>"
					+ result.VehicleIDStr;
			
			// Offset is the number of minutes ago that the bus location was
			// updated
			int tMinutes = Integer.parseInt(result.OffsetStr);
			String updated = "<b>" 
					+ getString(R.string.transitViewUpdated)
					+ "&nbsp;</b>"
					+ res.getQuantityString(
							R.plurals.transitViewUpdatedMinutes, tMinutes,
							tMinutes);
			
			String title = transitDestination + busNumber + "<br/>" + blockNumber;

			OverlayItem overlayvehicle = new OverlayItem(pointV, title,
					updated);
			if (result.DirectionStr.equals("EastBound")
					|| result.DirectionStr.equals("NorthBound")) {
				blue.addOverlay(overlayvehicle);
				blueint++;
			} else if (result.DirectionStr.equals("WestBound")
					|| result.DirectionStr.equals("SouthBound")) {
				red.addOverlay(overlayvehicle);
				redint++;
			} else {
				yellow.addOverlay(overlayvehicle);
				yellowint++;
			}
		}

		if (redint > 0) {
			mapOverlays.add(red);
		}
		if (blueint > 0) {
			mapOverlays.add(blue);
		}
		if (yellowint > 0) {
			mapOverlays.add(yellow);
		}
	}
	
	@Override
	public Object onRetainNonConfigurationInstance() {
	    return JSONString;
	}
	
	//GPS Functions
	@Override
	public void onLocationChanged(Location location) {
		mapOverlays.remove(gpsLoc);
		mapOverlays.add(gpsLoc);
		mapView.invalidate();
	}

	@Override
	public void onProviderDisabled(String provider) {
		mapOverlays.remove(gpsLoc);
		Toast.makeText(TransitViewActivity.this,
				"Turn on GPS, in your settings, to view your current location",
				Toast.LENGTH_LONG).show();
	}

	@Override
	public void onProviderEnabled(String provider) {
		mapOverlays.add(gpsLoc);
		mapView.invalidate();

	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		mapOverlays.remove(gpsLoc);
		locationManager.removeUpdates(this);
		gpsLoc.disableMyLocation();

	}

}
